module Rack
  # 
  # AllowFrom is Rack middleware, which allows whitelisted hosts to connect to your app
  #
  # MIT License - Adaptly (philip at adaptly dot com)
  #
  module AllowFrom
    class Middleware
      def initialize(app)
        @app = app
      end
    
      # 
      # Verify the client is allowed to connect, by checking env['REMOTE_ADDR'] or env['HTTP_X_REMOTE_HOSTNAME']
      # @param [Hash] env
      #
      # @option env [String] REMOTE_ADDR
      #   The client's IP address
      #
      # @option env [String] HTTP_X_REMOTE_HOSTNAME (nil)
      #   The client's purported host name
      def call(env)
        status, headers, body = @app.call(env)
        if valid_client?(env)
          [status, headers, body]
        else
          [403, {}, ["Unauthorized access"]]
        end
      end
    
      private
    
      def valid_client?(env)
        return true unless Rails.configuration.allow_from
        r = false
        ip = env['REMOTE_ADDR']
        host = env['HTTP_X_REMOTE_HOSTNAME']
        Rails.configuration.allow_from.each do |rule|
          if ip == rule || (host == rule && valid_ip?(ip, host))
            r = true
          elsif ip =~ %r{#{re_sub rule}} || (host =~ %r{#{re_sub rule}} && valid_ip?(ip, host))
            r = true
          end
          break if r
        end
        r
      end
    
      def valid_ip?(ip, host)
        require "socket"
        ips = TCPSocket.gethostbyname(host)[3..-1]
        ips.include?(ip)
      rescue => e
        #Rails.logger.debug("Rack::AllowFrom: Invalid hostname '#{host}'")
        false
      end

      def re_sub(re)
        re.gsub('.', '\\.').sub('*', '.*')
      end

    end
  end
end
